/*
 * Copyright 2011 Kim Lindhardt Madsen
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package dk.lindhardt.gwt.geie.client;

import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.Element;
import dk.lindhardt.gwt.geie.shared.CellFormat;
import dk.lindhardt.gwt.geie.shared.SerializableColor;

/**
 * User: AnAmuser
 * Date: 14-06-11
 * <p/>
 * Extracts style from a javascript element
 */
public class ElementStyleExtractor {

   private Element element;
   private float ie;
   private RegExp rgbPattern;
   private RegExp hexPattern;

   /**
    * A StyleExtractor
    * @param element the element to extract styling from
    */
   public ElementStyleExtractor(Element element) {
      this.element = element;
      this.ie = Util.getIEVersion();
      this.hexPattern = RegExp.compile("#([0-9A-Fa-f]{3,6})\\b");
      this.rgbPattern = RegExp.compile(
            "rgb\\(\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*,\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*,\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*\\)");
   }

   /**
    * Creates a new cell format with style from the elemet
    * @return the cell format
    */
   public CellFormat extractCellFormat() {
      CellFormat format = new CellFormat();
      mergeStyles(format);
      return format;
   }

   /**
    * Adds style from the element to the cell format
    * @param cellFormat the cell format
    */
   public void mergeStyles(CellFormat cellFormat) {
      setBorders(cellFormat);
      setBackgroundColor(cellFormat);
      setFont(cellFormat);
      setHorizontalAlignment(cellFormat);
      setVerticalAlignment(cellFormat);
   }

   /**
    * Sets the horizontal alignment from the text-align property
    */
   private void setVerticalAlignment(CellFormat cellFormat) {
      String verticalAlign = Util.getUsedStyle(ie, element,  getNativeStyleName("vertical-align"));
      if (verticalAlign.length() > 0) {
         if (verticalAlign.equals("top")) {
            cellFormat.setValignment(CellFormat.VAlignment.TOP);
         } else if (verticalAlign.equals("middle")) {
            cellFormat.setValignment(CellFormat.VAlignment.CENTER);
         } else if (verticalAlign.equals("bottom")) {
            cellFormat.setValignment(CellFormat.VAlignment.BOTTOM);
         }
      }
   }

   /**
    * Sets the horizontal alignment from the text-align property
    */
   private void setHorizontalAlignment(CellFormat cellFormat) {
      String textAlign = Util.getUsedStyle(ie, element,  getNativeStyleName("text-align"));
      if (textAlign.length() > 0) {
         if (textAlign.equals("left")) {
            cellFormat.setAlignment(CellFormat.Alignment.LEFT);
         } else if (textAlign.equals("center")) {
            cellFormat.setAlignment(CellFormat.Alignment.CENTER);
         } else if (textAlign.equals("right")) {
            cellFormat.setAlignment(CellFormat.Alignment.RIGHT);
         }
      }
   }

   /**
    * @param cellFormat Sets font on cell format. Finds first property in font family that is supported
    */
   private void setFont(CellFormat cellFormat) {
      String fontFamily = Util.getUsedStyle(ie, element,  getNativeStyleName("font-family"));
      for (String prop : fontFamily.split(",")) {
         String lowerCaseProp = prop.toLowerCase();
         if (lowerCaseProp.equals("sans-serif") || lowerCaseProp.equals("arial")) {
            cellFormat.setFont(CellFormat.Font.ARIAL);
            break;
         } else if (lowerCaseProp.equals("tahoma")) {
            cellFormat.setFont(CellFormat.Font.TAHOMA);
            break;
         } else if (lowerCaseProp.equals("serif") || lowerCaseProp.equals("times") ||
               lowerCaseProp.contains("times new roman")) {
            cellFormat.setFont(CellFormat.Font.TIMES);
            break;
         }
      }
      String fontSize = Util.getUsedStyle(ie, element,  getNativeStyleName("font-size"));
      cellFormat.setFontSize(Util.getPxSize(fontSize));
      String fontWeight = Util.getUsedStyle(ie, element,  getNativeStyleName("font-weight"));
      try {
         if (Integer.parseInt(fontWeight) > 400) {
            cellFormat.setBold(true);
         }
      } catch (Exception e) {
         if (fontWeight.contains("bold")) {
            cellFormat.setBold(true);
         }
      }
      String fontStyle = Util.getUsedStyle(ie, element,  getNativeStyleName("font-style"));
      if (fontStyle.equals("italic")) {
         cellFormat.setItalic(true);
      }
      String textDecoration = Util.getUsedStyle(ie, element,  getNativeStyleName("text-decoration"));
      if (textDecoration.equals("underline")) {
         cellFormat.setUnderlined(true);
      }
      String color = Util.getUsedStyle(ie, element, "color");
      cellFormat.setColor(getColor(color));
   }

   /**
    * @param cellFormat Sets background color on the cell format
    */
   private void setBackgroundColor(CellFormat cellFormat) {
      String backgroundColor = Util.getUsedStyle(ie, element,  getNativeStyleName("background-color"));
      cellFormat.setBackgroundColor(getColor(backgroundColor));
   }

   /**
    * @param cellFormat Sets borders on cell format
    */
   private void setBorders(CellFormat cellFormat) {
      setBorder(cellFormat, CellFormat.Border.LEFT);
      setBorder(cellFormat, CellFormat.Border.RIGHT);
      setBorder(cellFormat, CellFormat.Border.TOP);
      setBorder(cellFormat, CellFormat.Border.BOTTOM);
   }

   /**
    * Sets one border on cell format
    */
   private void setBorder(CellFormat cellFormat, CellFormat.Border border) {
      String borderName = border.name().toLowerCase();
      String borderStyle;
      String sBorderWidth;
      borderStyle = Util.getUsedStyle(ie, element, getNativeStyleName("border-" + borderName + "-style"));
      sBorderWidth = Util.getUsedStyle(ie, element, getNativeStyleName("border-" + borderName + "-width"));

      int borderWidth = Util.getPxSize(sBorderWidth);
      if (borderStyle.equals("solid")) {
         cellFormat.addBorder(border);
         cellFormat.setBorderType(border, CellFormat.BorderType.SOLID);
         cellFormat.setBorderWidth(border, borderWidth);
      } else if (borderStyle.equals("double")) {
         cellFormat.addBorder(border);
         cellFormat.setBorderType(border, CellFormat.BorderType.DOUBLE);
         cellFormat.setBorderWidth(border, borderWidth);
      } else if (borderStyle.equals("dotted")) {
         cellFormat.addBorder(border);
         cellFormat.setBorderType(border, CellFormat.BorderType.DOTTED);
         cellFormat.setBorderWidth(border, borderWidth);
      }
   }

   /**
    * If ie > 0 style names with "-" will be converted to camel case style names. Fx "font-size" becomes "fontSize" in
    * ie browsers.
    */
   private String getNativeStyleName(String styleName) {
      if (ie > 0) {
         String nativeStyleName = "";
         String[] split = styleName.split("-");
         for (int i = 0; i < split.length; i++) {
            String styleSplit = split[i];
            if (i == 0) {
               nativeStyleName += styleSplit;
            } else {
               char[] nameChars = styleSplit.toCharArray();
               nameChars[0] = Character.toUpperCase(nameChars[0]);
               nativeStyleName += String.valueOf(nameChars);
            }
         }
         return nativeStyleName;
      }

      return styleName;
   }

   /**
    * Finds a color in the given color string
    */
   private SerializableColor getColor(String colorString) {
      if (colorString != null) {
         if (rgbPattern.test(colorString)) {
            MatchResult match = rgbPattern.exec(colorString);
            int r = Integer.parseInt(match.getGroup(1));
            int g = Integer.parseInt(match.getGroup(2));
            int b = Integer.parseInt(match.getGroup(3));
            return new SerializableColor(r, g, b);
         } else if (hexPattern.test(colorString)) {
            return SerializableColor.hex2Rgb(colorString);
         }
      }
      return null;
   }

}
